package stud.mpgame.network;

import stud.mpgame.network.util.ResponseMessageDescription;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Класс, описывающий формат сообщения.
 *
 * Вы должны расширить данный абстрактный класс для описания своих форматов.
 *
 * @author f.pavel91@gmail.com
 * @version 1.4
 */
public abstract class MessageDescription {

    private static final Map< Integer, MessageDescription > registeredDescription = Collections.synchronizedMap(new HashMap< Integer, MessageDescription >());

    /**
     * Флаг, определяющий, будет ли посылаться ответное сообщение для данного {@link MessageDescription}.
     */
    protected boolean autoResponseFlag = false; //default value

    private Map< Integer, Class > registeredUserObjectIdentifies = new HashMap< Integer, Class >();
    private Map< Class, Integer > registeredUserObjectClasses = new HashMap< Class, Integer >();

    /**
     * Получение идентификатора сообщения.
     *
     * Каждый {@link MessageDescription} должен иметь ID - положительное целое число типа Integer.
     * Все ID должны быть уникальны для обеспечения корректной работы сетевой библиотеки.
     *
     * @return ID уникальный идентификатор типа сообщения.
     */
    abstract public int getID();

    /**
     *
     * @return состояние флага.
     */
    public boolean isAutoResponseFlag() {
        return autoResponseFlag;
    }

    /**
     * Метод для получения {@link MessageDescription} по его идентификатору.
     *
     * @param id уникальный идентификатор типа сообщения.
     * @return Соответствующий данному id {@link MessageDescription}.
     */
    public static MessageDescription getMessageDescription(int id) {
        return registeredDescription.get(id);
    }

    Set< Integer > getIdentifiers() {
        return registeredUserObjectIdentifies.keySet();
    }

    /**
     * Регистрация нового {@link MessageDescription}.
     *
     * @param messageDescription новый {@link MessageDescription}.
     */
    public static void registerDescription(MessageDescription messageDescription) {
        registeredDescription.put(messageDescription.getID(), messageDescription);
    }

    /**
     * Удаление данного messageDescription из базы данных.
     *
     * @param messageDescription удаляемый {@link MessageDescription}
     */
    public static void unRegisterDescription(MessageDescription messageDescription) {
        registeredDescription.remove(messageDescription.getID());
    }

    /**
     * Регистрация нового {@link UserObject} для данного {@link MessageDescription}.
     *
     * В пределах одного {@link MessageDescription} каждый {@link UserObject} должен иметь уникальный идентификатор.
     *
     * Например,
     * <pre>
     *     someMessageDescription.registerUserObject(someUserObjectId, SomeUserObject.class);
     * </pre>
     *
     * @param userObjectId уникальный идентификатор сообщения.
     * @param userObjectClass соответствующий данному {@link UserObject} класс.
     */
    protected void registerUserObject(int userObjectId, Class userObjectClass) {
        registeredUserObjectIdentifies.put(userObjectId, userObjectClass);
        registeredUserObjectClasses.put(userObjectClass, userObjectId);
    }

    /**
     * Удаление данного {@link UserObject} из данного {@link MessageDescription}.
     *
     * Например,
     * <pre>
     *     someMessageDescription.unRegisterUserObject(SomeUserObject.class);
     * </pre>
     *
     * @param userObjectClass соответствующий данному {@link UserObject} класс.
     */
    protected void unRegisterUserObject(Class userObjectClass) {
        registeredUserObjectIdentifies.remove(registeredUserObjectClasses.get(userObjectClass));
        registeredUserObjectClasses.remove(userObjectClass);
    }

    UserObject createUserObject(int id) throws Exception {
        Class userObjectClass = registeredUserObjectIdentifies.get(id);
        return (UserObject) userObjectClass.newInstance();
    }

    /**
     *
     * @param userObjectClass класс данного {@link UserObject}.
     * @return Идентификатор данного {@link UserObject}.
     */
    public int getUserObjectId(Class userObjectClass) {
        return registeredUserObjectClasses.get(userObjectClass);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        MessageDescription that = (MessageDescription) o;

        return this.getID() == that.getID();
    }

}